/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_MESH_IO_H
#define _GCTL_MESH_IO_H

// library's head files
#include "../core.h"
#include "../geometry.h"
#include "../utility.h"

#include "triangle_io.h"


namespace gctl
{
    #define DEFAULT_INVALID_TAG -9999

    enum element_type_enum
    {
        NoSet,
        _2NodeLine,
        _3NodeTriangle,
        _4NodeQuadrangle,
        _4NodeTetrahedron,
        _8NodeHexahedron,
        _6NodePrism,
        _5NodePyramid,
        _3NodeSecondOrderLine,
        _6NdoeSecondOrderLine,
        _9NodeSecondOrderQuadrangle,
        _10NodeSecondOrderTetrahedron,
        _27NodeSecondOrderHexahedron,
        _18NodeSecondOrderPrism,
        _14NodeSecondOrderPyramid,
        _1NodePoint,
        _8NodeSecondOrderQuadrangle,
        _20NdoeSecondOrderHexahedron,
        _15NodeSecondOrderPrism,
        _13NodeSecondOrderPyramid,
        _9NodeThirdOrderIncompleteTriangle,
        _10NdoeThirdOrderTriangle,
        _12NodeFourthOrderIncompleteTriangle,
        _15NodeFourthOrderTriangle,
        _15NodeFifthOrderCompleteTriangle,
        _21NodeFifthOrderCompleteTriangle,
        _4NodeThirdOrderEdge,
        _5NodeFourthOrderEdge,
        _6NodeFifthOrderEdge,
        _20NodeThirdOrderTetrahedron,
        _35NodeFourthOrderTetrahedron,
        _56NodeFifithOrderTetrahedron,
        _64NodeThirdOrderHexahedron,
        _125NodeFourthOrderHexahedron,
    };

    enum element_tag_enum
    {
        PhysicalTag,
        GeometryTag,
        PartitionTag,
        NodeTag,
    };

    struct mesh_element
    {
        bool enabled;
        size_t id;
        int type_tag;
        element_type_enum type;
        array<vertex3dc*> vert_ptrs;
        array<mesh_element*> neigh_ptrs;
        array<int> int_tag;

        mesh_element();
    };

    struct mesh_data
    {
        bool enabled;
        mesh_data_type_e d_type;
        array<std::string> str_tag;
        array<double> real_tag;
        array<int> int_tag;
        array<vertex3dc*> vert_ptrs;
        array<mesh_element*> elem_ptrs;
        array<double> val;

        mesh_data();
        void clear();
    };

    struct mesh_element_group
    {
        bool enabled;
        int type_tag;
        int phys_group;
        int geom_group;
        int part_group;
        std::vector<mesh_element*> elem_ptrs;

        mesh_element_group();
    };

    struct mesh_physical_group
	{
        bool enabled;
		size_t dim_tag, phys_tag;
		std::string name;

        mesh_physical_group();
	};
    
    class mesh_io
    {
    public:
        mesh_io();
        virtual ~mesh_io();

        void reset();
        void info(std::ostream &ss = std::clog);

        void enable_data(std::string dataname = "null");
        void disable_data(std::string dataname = "null");
        void enable_element(element_type_enum elem_type = NoSet);
        void enable_element(std::string phys_name);
        void enable_element(int tag, element_tag_enum tag_type);
        void disable_element(element_type_enum elem_type = NoSet);
        void disable_element(std::string phys_name);
        void disable_element(int tag, element_tag_enum tag_type);

        const array<int> &get_node_tag();
        const array<vertex3dc> &get_nodes();
        const array<mesh_element> &get_elems();
        void export_tags_to_data(element_tag_enum tag_type);
        void export_elements_to(array<triangle> &tris);
        void export_elements_to(array<tetrahedron> &tets);

        void read_triangle_ascii(std::string filename, index_packed_e is_packed = Packed);
        void read_gmsh_v2_ascii(std::string filename, index_packed_e is_packed = NotPacked);
        void save_gmsh_v2_ascii(std::string filename, index_packed_e is_packed = NotPacked);
        void save_vtk_legacy_ascii(std::string filename);
        void export_data_to_lld(std::string filename, std::string dataname = "null", double refr = GCTL_Earth_Radius, double refR = GCTL_Earth_Radius);
    
    private:
        bool initialized_;

        size_t valid_node_size, valid_elem_size, valid_phys_size;
        array<vertex3dc> nodes_;
        array<mesh_element> elems_;
        std::vector<mesh_data> datas_;
        array<mesh_physical_group> phys_;
        std::vector<mesh_element_group> groups_;
        array<int> nodes_tag_;
        
        element_type_enum mesh_type_[94];
        int mesh_type_size_[94];
        std::string mesh_type_name_[94];
        int mesh2vtk_type_tag_[7];
        int vtk2mesh_type_tag_[14];

        void update_indexing();
    };
}

#endif // _GCTL_MESH_IO_H